篇首语:本文由编程笔记#小编为大家整理,主要介绍了音视频开发9. 使用ffmpeg 将pcm转码mp3实践(C++)相关的知识,希望对你有一定的参考价值。
@[TOC](音视频开发9. 使用ffmpeg 将pcm转码mp3实践(C++))
如前文所述,pcm是音频裸数据,要转成mp3需要进行重采样、编码步骤。常见的PCM格式有8位和16位两种。
采样频率指每秒钟对音频的采样点数,单位为Hz(赫兹)。
如采样频率为44100hz是指每秒钟采集44100个样本点。
常见的声道数有:
例:
ffmpeg -y -ac 1 -ar 16000 -f s16le -i /data/ffmpeg/test/input.pcm -c:a libmp3lame -q:a 2 /data/ffmpeg/test/output.mp3
三、几个重要函数
示例:
SwrContext* swrContext = NULL;
// 设置参数, 1. 重采样上下文 2.输出声道布局 4.输出采样率, 5.输入声道布局 6.输入样本格式 7.输入采样率 8.配音 9.日志
swrContext = swr_alloc_set_opts(swrContext, avCodecContext->channel_layout, avCodecContext->sample_fmt, avCodecContext->sample_rate,
AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, 44100,
0, 0);
示例:
SwrContext *swrContext = swr_alloc();
// 通道布局:立体声
av_opt_set_int(swrContext, "in_channel_layout", AV_CH_LAYOUT_STEREO, 0);
// 采样率:44100
av_opt_set_int(swrContext, "in_sample_rate", OSR, 0);
// 样本格式 s16交错存储
av_opt_set_sample_fmt(swrContext, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
在设置完参数后,要调用swr_init进行初始化。
根据音频格式分配相应大小的内存空间,函数内部会调用 av_samples_alloc
,示例代码:
result = av_samples_alloc_array_and_samples(&input_data, &input_linesize, 2, avFrame->nb_samples, AV_SAMPLE_FMT_S16, 0);
根据音频格式分配相应大小的内存空间。用于转换过程中对输出内存大小进行调整。
cmake_minimum_required(VERSION 3.17)
project(ffmpeg_demo)
# 设置ffmpeg依赖库及头文件所在目录,并存进指定变量
set(ffmpeg_libs_DIR /home/xundh/ffmpeg_sources/ffmpeg-4.2.2)
set(ffmpeg_headers_DIR /home/xundh/ffmpeg_sources/ffmpeg-4.2.2)
#对于find_package找不到的外部依赖库,可以用add_library添加
# SHARED表示添加的是动态库
# IMPORTED表示是引入已经存在的动态库
add_library( avcodec SHARED IMPORTED)
add_library( avfilter SHARED IMPORTED )
add_library( swresample SHARED IMPORTED )
add_library( swscale SHARED IMPORTED )
add_library( avformat SHARED IMPORTED )
add_library( avutil SHARED IMPORTED )
#指定所添加依赖库的导入路径
set_target_properties( avcodec PROPERTIES IMPORTED_LOCATION $ffmpeg_libs_DIR/libavcodec/libavcodec.so )
set_target_properties( avfilter PROPERTIES IMPORTED_LOCATION $ffmpeg_libs_DIR/libavfilter/libavfilter.so )
set_target_properties( swresample PROPERTIES IMPORTED_LOCATION $ffmpeg_libs_DIR/libswresample/libswresample.so )
set_target_properties( swscale PROPERTIES IMPORTED_LOCATION $ffmpeg_libs_DIR/libswscale/libswscale.so )
set_target_properties( avformat PROPERTIES IMPORTED_LOCATION $ffmpeg_libs_DIR/libavformat/libavformat.so )
set_target_properties( avutil PROPERTIES IMPORTED_LOCATION $ffmpeg_libs_DIR/libavutil/libavutil.so )
# 添加头文件路径到编译器的头文件搜索路径下,多个路径以空格分隔
include_directories( $ffmpeg_headers_DIR )
link_directories($ffmpeg_libs_DIR )
link_directories(/usr/lib)
set(CMAKE_CXX_STANDARD 14)
# add_executable(ffmpeg_demo main.cpp)
add_executable(ffmpeg_demo pcm_to_mp3.cpp)
target_link_libraries($PROJECT_NAME avcodec avformat avutil swresample swscale swscale avfilter )
#include <iostream>
#ifdef __cplusplus
extern "C"
#endif
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/imgutils.h"
#include "libavutil/log.h"
#include "libswresample/swresample.h"
#include "libavutil/avutil.h"
#include "libavutil/opt.h"
#ifdef __cplusplus
#endif
using namespace std;
#define CHANNEL 2
#define OSR 44100
/**
* pcm 转 mp3格式,输入文件路径
*/
int pcm_to_mp3(const char *pcm_file_path, const char *mp3_file_path)
FILE *pcm_file &#61; NULL;
FILE *mp3_file &#61; NULL;
int result;
// 获取mp3编码器
cout << "获取mp3编码器" << endl;
const AVCodec *avCodec &#61; avcodec_find_encoder(AV_CODEC_ID_MP3);
if (!avCodec)
cout << "初始化mp3 编码器失败" << endl;
return -1;
// 创建编码器上下文
AVCodecContext *avCodecContext &#61; avcodec_alloc_context3(avCodec);
if (!avCodecContext)
cout << "avcodec_alloc_context3 失败" << avCodecContext << endl;
return -1;
avCodecContext->bit_rate &#61; 64000;
avCodecContext->channels &#61; CHANNEL;
avCodecContext->channel_layout &#61; AV_CH_LAYOUT_STEREO;
avCodecContext->sample_rate &#61; OSR;
avCodecContext->sample_fmt &#61; AV_SAMPLE_FMT_S16P;
avCodecContext->time_base &#61; av_get_time_base_q();
// 打开编码器
cout << "打开mp3编码器" << endl;
result &#61; avcodec_open2(avCodecContext, avCodec, NULL);
if (result < 0)
cout << "avcodec_open2失败: " << result << endl;
return result;
cout << "打开mp3文件" << mp3_file_path << endl;
// 打开输出文件
mp3_file &#61; fopen(mp3_file_path, "wb");
if (!mp3_file)
cout << "打开mp3文件失败" << endl;
return -1;
// AVFrame 接受重采样的每一帧的音频数据 每帧的样本大小为1152
AVFrame *avFrame &#61; av_frame_alloc();
if (!avFrame)
cout << "分配avFrame帧失败" << endl;
return -1;
// mp3一帧的样本数为1152
avFrame->nb_samples &#61; 1152;
avFrame->channels &#61; CHANNEL;
avFrame->channel_layout &#61; AV_CH_LAYOUT_STEREO;
avFrame->format &#61; AV_SAMPLE_FMT_S16P;
// 给帧分配内存空间
result &#61; av_frame_get_buffer(avFrame, 0);
if (result < 0)
cout << "分配帧内存失败" << endl;
return result;
// 重采样 创建音频重采样上下文
cout << "配置重采样器上下文" << endl;
SwrContext *swrContext &#61; swr_alloc();
if (!swrContext)
cout << "配置重采样上下文失败" << endl;
return -1;
// 设置重采样输入pcm参数:通道布局:立体声 采样率:44100 样本格式 s16交错存储
av_opt_set_int(swrContext, "in_channel_layout", AV_CH_LAYOUT_STEREO, 0);
av_opt_set_int(swrContext, "in_sample_rate", OSR, 0);
av_opt_set_sample_fmt(swrContext, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
// 设置重采样输出mp3参数:通道布局:立体声 采样率:44100 样本格式 s16平面存储
av_opt_set_int(swrContext, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0);
av_opt_set_int(swrContext, "out_sample_rate", OSR, 0);
av_opt_set_sample_fmt(swrContext, "out_sample_fmt", AV_SAMPLE_FMT_S16P, 0);
// 重采样初始化
result &#61; swr_init(swrContext);
if (result < 0)
cout << "重采样器初始化失败,error&#61;" << result << endl;
return result;
uint8_t **input_data &#61; NULL;
uint8_t **output_data &#61; NULL;
int input_linesize, output_linesize;
// 打开pcm文件
cout << "打开源 pcm 文件 " << pcm_file_path << endl;
pcm_file &#61; fopen(pcm_file_path, "rb");
if (!pcm_file)
cout << "打开 pcm 文件失败" << endl;
return -1;
cout << "开始编码转换" << endl;
// 给pcm文件数据分配空间
result &#61; av_samples_alloc_array_and_samples(&input_data, &input_linesize, 2, avFrame->nb_samples, AV_SAMPLE_FMT_S16, 0);
if (result < 0)
cout << "给pcm文件分配空间失败, result &#61; " << result << endl;
return result;
// 缓存重采样数据的空间分配
result &#61; av_samples_alloc_array_and_samples(&output_data, &output_linesize, 2, avFrame->nb_samples, AV_SAMPLE_FMT_S16P, 0);
if (result < 0)
cout << "获取mp3 重采样数据失败, result&#61;" << result << endl;
return result;
// 存放编码后的数据
AVPacket *avPacket &#61; av_packet_alloc();
if (!avPacket)
cout << "分配 avPacket 内存失败" << endl;
return -1;
cout << "&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;循环读入帧&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;" << endl;
long total_size &#61; 0;
while (!feof(pcm_file))
long read_size &#61; (long)fread(input_data[0], 1, avFrame->nb_samples * 4, pcm_file);
total_size &#43;&#61; read_size;
if ((total_size / read_size) % 50 &#61;&#61; 0)
cout << "读取数据:" << read_size << "字节; 累计:" << total_size << " 字节 " << endl;
if (read_size <&#61; 0)
break;
// 重采样
result &#61; swr_convert(swrContext, output_data, avFrame->nb_samples, (const uint8_t **)input_data, avFrame->nb_samples);
if (result < 0)
cout << "音频编码失败,错误信息" << result << endl;
return result;
// 将重采样后的数据存入frame&#xff0c;MP3是s16p 先存放左声道的数据 后存放右声道的数据&#xff0c; data[0]是左声道&#xff0c;1是右声道
avFrame->data[0] &#61; output_data[0];
avFrame->data[1] &#61; output_data[1];
// 编码&#xff0c;写入mp3文件&#xff0c;实际上是对frame这个结构体里面的数据进行编码操作,发送到编码线程:使用编码器 和 存储数据的frame
result &#61; avcodec_send_frame(avCodecContext, avFrame);
if (result < 0)
cout << "mp3编码失败,错误信息:" << result << endl;
return result;
while (result >&#61; 0)
// 接收编码后的数据&#xff0c;使用编码器 和 存储编码数据的pkt, 有可能需要多次才能接收完成
result &#61; avcodec_receive_packet(avCodecContext, avPacket);
// AVERROR_EOF表示没有数据了 这两个错误不影响继续接收数据
if (result &#61;&#61; AVERROR_EOF || result &#61;&#61; AVERROR(EAGAIN))
continue;
else if (result < 0)
break;
fwrite(avPacket->data, 1, avPacket->size, mp3_file);
av_packet_unref(avPacket);
// 告诉解码器没有帧了,如果没有这几行的逻辑&#xff0c;在关闭 avCodecContext 可能会提示 * fames left in the queu on closing
avcodec_send_frame(avCodecContext, __null);
while(avcodec_receive_packet(avCodecContext, avPacket)!&#61;AVERROR_EOF);
// 关闭缓存
if (input_data)
av_free(input_data);
if (output_data)
av_free(output_data);
cout << "关闭文件" << endl;
fclose(pcm_file);
fclose(mp3_file);
cout << "释放资源" << endl;
// s释放 frame pkt
av_frame_free(&avFrame);
av_packet_free(&avPacket);
// 释放重采样上下文
swr_free(&swrContext);
// 释放编码器上下文
avcodec_free_context(&avCodecContext);
cout << "转码完成" << endl;
return 0;
int main(int argc, char *argv[])
const char *input &#61; "input.pcm";
const char *output &#61; "out.mp3";
pcm_to_mp3(input, output);
return 1;
程序执行结果&#xff1a;
./ffmpeg_demo
获取mp3编码器
打开mp3编码器
打开mp3文件out.mp3
配置重采样器上下文
打开源 pcm 文件 input.pcm
开始编码转换
&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;循环读入帧&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;
读取数据:4608字节; 累计:230400 字节
读取数据:4608字节; 累计:460800 字节
读取数据:4608字节; 累计:691200 字节
读取数据:4608字节; 累计:921600 字节
关闭文件
释放资源
转码完成